package com.blindnetworking;

import java.math.BigInteger;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.CompositeFilterOperator;
import com.google.appengine.api.datastore.Query.Filter;
import com.google.appengine.api.datastore.Query.FilterPredicate;
import com.google.appengine.api.datastore.Query.FilterOperator;

public class DBUtil {

	private static DatastoreService service = DatastoreServiceFactory.getDatastoreService();

	private static String[] venues= new String[]{
		"Near the cofee machine",
		"At the giant pin",
		"Under the yellow beach umbrella"
	};

	private static String[] starters = new String[]{
		"Favorite Programming Languages",
		"Native vs Mobile",
		"Night & coffee or day & sun",
		"What do you think about in the bathroom",
		"Operating Systems: best for desktop and est for mobile",
		"Best thing in the conference yet"
	};
	
	private static SecureRandom random = new SecureRandom();

	public static String generateRandomString(){
		return new BigInteger(130, random).toString(32);
	}
	
	public static boolean createNewUser(String name, String email, String twitter, String wear){
		Entity e = null;
		try{
			e = new Entity("User");
			e.setProperty("name", name);
			e.setProperty("email", email);
			e.setProperty("twitter", twitter);
			e.setProperty("wear", wear);
			
			service.put(e);
			
			MailUtil.sendWelcomeMail(name, email);
			
		}catch(Exception ex){
			ex.printStackTrace();
			
			if (e != null)
				service.delete(e.getKey());
			
			return false;
		}
		return true;
	}
	
	public static List<Entity> getCandidateUsers(){
		Query q = new Query("User");
		PreparedQuery pq = service.prepare(q);
		Iterator<Entity> it = pq.asIterator();
		
		List<Entity> result = new ArrayList<Entity>();
		while(it.hasNext()){
			Entity e = it.next();
			
			Filter u1Filter = new FilterPredicate("user1", FilterOperator.EQUAL, e.getKey());
			Filter u2Filter = new FilterPredicate("user2", FilterOperator.EQUAL, e.getKey());
				
			//meetings from user
			Filter meetingsFromUser = CompositeFilterOperator.or(u2Filter, u1Filter);

//			//pending
//			Filter pending = new FilterPredicate("invitePending", FilterOperator.NOT_EQUAL, true);
			
//			//newer then xx minutes
//			Calendar c = new GregorianCalendar();
//			c.setTime(new Date());
//			c.add(Calendar.MINUTE, -Consts.MINUTES_TO_REPLY_INVITATION);
//			Filter newer = new FilterPredicate("inviteSendingDate", FilterOperator.GREATER_THAN, c.getTime());
//			
//			// all users without pending meetings or meeting newer then xx minutes 
//			Filter final filter = 
			
			
			Query mq = new Query("Meeting");
			mq.setFilter(meetingsFromUser);
			
			PreparedQuery pqm = service.prepare(mq);
			Iterator<Entity> iterator = pqm.asIterable().iterator();

			boolean candidate = true;
			
			while (iterator.hasNext()){
				Entity meeting = iterator.next();
				
				String state = (String)meeting.getProperty("state");
				
				//do not include user if has open meetings
				if (!state.equals("declined") || !state.equals("done")){
					candidate = false;
					break;
				}
				
				//do not include if user had a meeting a short time ago
				
				Date meetingDate = (Date)meeting.getProperty("meetingDate");
				Calendar c = new GregorianCalendar();
				c.setTime(new Date());
				c.add(Calendar.MINUTE, -Consts.MINUTES_BETWEEN_MEETINGS);
				
				if (state.equals("done") && meetingDate.after(c.getTime())){
					candidate=false;
					break;
				}
			}
			
			if(candidate)
				result.add(e);
			
		}
		
		return result;
	}

	public static String getRandomVenue(){
		Random r = new Random();
		return venues[r.nextInt(venues.length)];
	}
	
	public static String getRandomStarter(){
		Random r = new Random();
		return starters[r.nextInt(starters.length)];
	}
	
	
	public static Entity createMeeting(Entity u1, Entity u2) {
		Entity e = null;
		
		Calendar c = new GregorianCalendar();
		c.setTime(new Date());
		c.add(Calendar.DAY_OF_YEAR, Consts.MEETING_SCHEDULE_INTERVAL);
		
		try{
			e = new Entity("Meeting");
			e.setProperty("user1", u1.getKey());
			e.setProperty("user2", u2.getKey());
			e.setProperty("user2ConfirmKey", generateRandomString());
			e.setProperty("user1ConfirmKey", generateRandomString());
			
			e.setProperty("meetingDate", c.getTime());
			e.setProperty("venue", getRandomVenue());
			e.setProperty("starter", getRandomStarter());
			e.setProperty("state","pending");
			e.setProperty("inviteSentDate",new Date());
			
			service.put(e);
			
			System.out.println(e);
			
		}catch (Exception ex){
			ex.printStackTrace();
			if (e != null)
				service.delete(e.getKey());
		}
		
		return e;
	}

	public static Entity accept(String key, boolean accept) throws Exception {
		Query mq = new Query("Meeting");
		Filter u1Filter = new FilterPredicate("user1ConfirmKey", FilterOperator.EQUAL, key);
		Filter u2Filter = new FilterPredicate("user2ConfirmKey", FilterOperator.EQUAL, key);
		Filter pendingFilter = new FilterPredicate("state", FilterOperator.EQUAL, "pending");
			
		//meetings from key
		Filter meetingsFromKey = CompositeFilterOperator.or(u2Filter, u1Filter);
		
		//pending
		Filter f = CompositeFilterOperator.and(meetingsFromKey, pendingFilter);
		mq.setFilter(f);
		
		PreparedQuery pqm = service.prepare(mq);
		Iterator<Entity> iterator = pqm.asIterable().iterator();

		Entity meeting = null;
		
		while(iterator.hasNext()){
			meeting = iterator.next();
			
			System.out.println(key);
			System.out.println("New meeting returned:" + meeting );
			
			if (eq(meeting,"user1ConfirmKey",key)){
				meeting.setProperty("user1Accepted", accept);
			}else{
				meeting.setProperty("user2Accepted", accept);
			}
					
			if (eq(meeting,"user1Accepted",true) && eq(meeting,"user2Accepted",true)){
				meeting.setProperty("state", "done");
			}
			
			if (eq(meeting,"user1Accepted",false) || eq(meeting,"user2Accepted",false)){
				meeting.setProperty("state", "declined");
			}

			if (eq(meeting,"state","declined") || eq(meeting,"state","done")){
				Entity u1 = getUser1(meeting);
				Entity u2 = getUser2(meeting);

				if (u1 != null && u2!=null)
					MailUtil.sendFinalConfirmationEmails(u1,u2,meeting);

			}
			
			service.put(meeting);

		}
		return meeting;
	}
	
	public static Entity getUser1(Entity meeting){
		try {
			return service.get((Key)meeting.getProperty("user1"));
		} catch (EntityNotFoundException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public static Entity getUser2(Entity meeting){
		try {
			return service.get((Key)meeting.getProperty("user2"));
		} catch (EntityNotFoundException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public static Entity getMeetingFromKey(String key){
		Query mq = new Query("Meeting");
		Filter u1Filter = new FilterPredicate("user1ConfirmKey", FilterOperator.EQUAL, key);
		Filter u2Filter = new FilterPredicate("user2ConfirmKey", FilterOperator.EQUAL, key);
		Filter meetingsFromKey = CompositeFilterOperator.or(u2Filter, u1Filter);
		
		mq.setFilter(meetingsFromKey);
		
		Entity meeting = null;
		PreparedQuery pqm = service.prepare(mq);
		Iterator<Entity> iterator = pqm.asIterator();
		while (iterator.hasNext()){
			meeting = iterator.next();
		}
		return meeting;

	}
	
	public static boolean eq(Entity e, String property, Object value){
		Object v = e.getProperty(property);
		if (v == null)
			return false;
		return v.equals(value);
	}
	
	public static String formatDate(Date date){
		SimpleDateFormat dt = new SimpleDateFormat("yyyy-mm-dd hh:mm");
		return dt.format(date);
	}
}
